package main

import (
	"bufio"
	j_log "config-client-v1/test/tcp-server/j-log"
	"config-client-v1/test/tcp-server/pkg"
	"config-client-v1/utils/tools"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net"
	"strings"
	"sync"
	"time"
)

const BYTE_LENGTH = 8192 // 单次循环最大读取的字节数量, 如果单次发送的数据包大于这里, 则需要多次读取

type Client struct {
	RemoteAddr   string // 客户端 IP + 端口
	Connection   net.Conn
	Reader       *bufio.Reader
	CreateTime   time.Time // 连接时间
	LastDataTime time.Time // 最近一次接收数据的时间
}

type OnlineMapStu struct {
	M          map[string]*Client
	Register   chan *Client
	Unregister chan *Client
	sync.RWMutex
}

var OnlineMap OnlineMapStu
var serverPort string
var sendRate int
var jsonFile *string
var list []*pkg.FrameItemParam

func main() {
	port := flag.String("port", "6666", "监听端口")
	rate := flag.Int("rate", 3000, "发送频率")
	jsonFile = flag.String("json_file", "frame.json", "帧格式json文件")
	flag.Parse()

	err := loadingFrameItem()
	if err != nil {
		j_log.LogError([]interface{}{"加载帧格式失败", err.Error()})
		return
	}

	serverPort = *port
	sendRate = *rate
	if sendRate <= 50 {
		sendRate = 100
	}

	OnlineMap = OnlineMapStu{}
	OnlineMap.M = make(map[string]*Client)
	OnlineMap.Register = make(chan *Client, 1000)
	OnlineMap.Unregister = make(chan *Client, 1000)

	go sendMessageTest()
	go connectionPoolManage()

	addr := fmt.Sprintf("0.0.0.0:%s", serverPort)
	listener, err := net.Listen("tcp", ":"+serverPort)
	if err != nil {
		j_log.LogError([]interface{}{"tcp 服务启动失败", err.Error()})
		return
	}
	j_log.LogInfo([]interface{}{"tcp 服务启动成功, 已监听端口", addr})
	for {
		conn, err2 := listener.Accept()
		if err2 != nil {
			j_log.LogError([]interface{}{"listener.Accept", err2.Error()})
			break
		}
		remoteAddr := conn.RemoteAddr().String()
		reader := bufio.NewReader(conn)
		client := &Client{
			RemoteAddr:   remoteAddr,
			Connection:   conn,
			Reader:       reader,
			CreateTime:   time.Now(),
			LastDataTime: time.Now(),
		}
		OnlineMap.Register <- client
		//_ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
		go connectionHandle(client)
	}
}

func sendMessageTest() {
	for {
		data, err0 := createFrameRawData()
		if err0 != nil {
			j_log.LogError([]interface{}{"生成数据帧失败", err0.Error()})
			return
		}
		str := tools.ByteArrayToHexStr(data)
		OnlineMap.RLock()
		for _, client := range OnlineMap.M {
			n, err := client.Connection.Write(data)
			if err != nil {
				j_log.LogError([]interface{}{"tcp写入失败", err.Error()})
				continue
			}
			j_log.LogInfo([]interface{}{fmt.Sprintf("【send】%s# len=%d : %s", client.RemoteAddr, n, str)})
		}
		OnlineMap.RUnlock()
		time.Sleep(time.Second * 3)
	}
}

func connectionHandle(client *Client) {
	defer func() {
		if err := recover(); err != nil {
			j_log.LogError([]interface{}{"connectionHandle", err})
		}
		OnlineMap.Unregister <- client
	}()
	remoteAddr := client.Connection.RemoteAddr().String() // 客户端IP:PORT
	var buffer [BYTE_LENGTH]byte
	for {
		n, err := client.Connection.Read(buffer[0:])
		if err != nil {
			if err == net.ErrClosed || strings.Contains(err.Error(), "use of closed network connection") || strings.Contains(err.Error(), "read: connection reset by peer") || strings.Contains(err.Error(), "forcibly closed") {
				j_log.LogError([]interface{}{"连接已关闭或重置", remoteAddr})
			} else {
				j_log.LogError([]interface{}{"unknown read error:", err.Error()})
			}
			return
		}
		OnlineMap.Register <- client

		str := tools.ByteArrayToHexStr(buffer[0:n])
		//_ = tools.WriteStringToLog(fmt.Sprintf("[%s ~ %s]# %s", tools.GetNow(), remoteAddr, str), "well_raw_data.log", "")
		/* --- 处理 rawData 开始 --- */
		j_log.LogInfo([]interface{}{fmt.Sprintf("【recv】%s# len=%d : %s", remoteAddr, n, str)})
		/* --- 处理 rawData 结束 --- */
		clearBuffer(&buffer, n)

	}
}

// 连接管理, 检查连接是否在线, 删除离线的client
func connectionPoolManage() {
	// 连接和断开管理
	go func() {
		for {
			select {
			case client := <-OnlineMap.Register:
				OnlineMap.RLock()
				_, ok := OnlineMap.M[client.RemoteAddr]
				if !ok {
					j_log.LogInfo([]interface{}{"新客户端已连接", client.RemoteAddr})
				}
				OnlineMap.RUnlock()
				client.LastDataTime = time.Now()
				OnlineMap.Lock()
				OnlineMap.M[client.RemoteAddr] = client
				OnlineMap.Unlock()
			case client := <-OnlineMap.Unregister:
				j_log.LogInfo([]interface{}{"客户端已离线", client.RemoteAddr})
				_ = client.Connection.Close()
				OnlineMap.Lock()
				delete(OnlineMap.M, client.RemoteAddr)
				OnlineMap.Unlock()
			}
		}
	}()

	// 超过一定时间未发送数据, 自动算离线; 1秒一次循环判断, 删除最后一次发送数据的时间大于5秒的client
	//go func() {
	//	for {
	//		time.Sleep(1 * time.Second)
	//		now := time.Now()
	//		OnlineMap.RLock()
	//		for _, client := range OnlineMap.M {
	//			if now.Sub(client.LastDataTime) > time.Duration(timeoutKick)*time.Second {
	//				fmt.Println("客户端长时间未发送数据, 已掉线: ", client.RemoteAddr)
	//				OnlineMap.Unregister <- client
	//			}
	//		}
	//		OnlineMap.RUnlock()
	//	}
	//}()
}

func clearBuffer(buf *[BYTE_LENGTH]byte, n int) {
	if n >= BYTE_LENGTH {
		n = BYTE_LENGTH
	}
	for i := 0; i < n; i++ {
		buf[i] = 0
	}
}

func createFrameRawData() (data []byte, err error) {
	data = make([]byte, 0)
	uniqueKeyBoolMap := make(map[string]bool)
	for i := 0; i < len(list); i++ {
		val := list[i]
		if list[i].Start > list[i].End {
			err = errors.New("开始字节不可大于结束字节")
			return
		}
		if i > 0 && len(list) > 1 {
			if !uniqueKeyBoolMap[fmt.Sprintf("%d", val.Start)] && list[i].Start != (list[i-1].End+1) {
				err = errors.New(fmt.Sprintf("[%s] 和前面字段不连续 %d~%d", val.FieldName, val.Start, val.End))
				return
			}
		}
		l := val.End - val.Start + 1
		if val.InputMethod == 1 {
			switch val.SpecType {
			case "bool":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 1)))
			case "uint8":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(0, 255)))
			case "int8":
				if l != 1 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				data = append(data, uint8(pkg.GetRandAreaNumber(-128, 127)))
			case "uint16":
				if l != 2 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint16(pkg.GetRandAreaNumber(0, 65535))
				tmpB := tools.Uint16ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "int16":
				if l != 2 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint16(pkg.GetRandAreaNumber(-32768, 32767))
				tmpB := tools.Uint16ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "uint32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := uint32(pkg.GetRandAreaNumber(200000, 300000))
				tmpB := tools.Uint32ToBytes(tmpV, "big")
				data = append(data, tmpB[:]...)
			case "int32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := int32(pkg.GetRandAreaNumber(100000, 150000))
				tmpB := tools.Uint32ToBytes(uint32(tmpV), "big")
				data = append(data, tmpB[:]...)
			case "float32":
				if l != 4 {
					err = errors.New(fmt.Sprintf("%s 占用字节异常", val.FieldName))
					return
				}
				tmpV := float64(pkg.GetRandAreaNumber(-100000, 100000)) / 100.0
				tmpB := tools.Float32ToBytes4(float32(tmpV), val.Endian)
				data = append(data, tmpB[:]...)
			default:
				err = errors.New(fmt.Sprintf("%s spec_type 参数无效", val.FieldName))
				return
			}
			continue
		}
		if val.Type == "校验" {
			if val.CheckStart > val.End {
				err = errors.New("开始字节不可大于结束字节")
				return
			}
			if val.CheckStart >= len(data) {
				err = errors.New("校验起始字节异常")
				return
			}
			if val.CheckEnd >= len(data) {
				fmt.Printf("val.CheckEnd=%d,len(data)=%d\n", val.CheckEnd, len(data))
				err = errors.New("校验结束字节异常")
				return
			}

			switch val.CheckMethod {
			case "sum":
				if l != 1 {
					err = errors.New("sum校验和最多占用一个字节")
					return
				}
				byteSum := pkg.GetBytesSum(data, val.CheckStart, val.CheckEnd)
				data = append(data, byteSum)
			case "crc16":
				if l != 2 {
					err = errors.New("crc16校验最多占用两个字节")
					return
				}
				bytes := pkg.GetCrc16Result(data[val.CheckStart : val.CheckEnd+1])
				data = append(data, bytes...)
			default:
				err = errors.New("校验方法参数无效")
				return
			}
			continue
		}
		if val.RawData == "" {
			err = errors.New("非变量且非校验的raw_data不可为空")
			return
		} else {
			b, err2 := pkg.HexStrToByteArray(val.RawData)
			if err2 != nil {
				err = errors.New(fmt.Sprintf("%s  rawData值格式错误 %s", val.FieldName, err2.Error()))
				return
			}
			if len(b) != l {
				err = errors.New(fmt.Sprintf("%s  rawData值长度与占用字节不匹配", val.FieldName))
				return
			}
			data = append(data, b...)
		}
	}
	return
}

func loadingFrameItem() (err error) {
	str, err := pkg.ReadFileContent(*jsonFile)
	if err != nil {
		err = errors.New(fmt.Sprintf("读取帧格式文件失败 %s\n", err))
		return
	}
	list = make([]*pkg.FrameItemParam, 0)
	err = json.Unmarshal([]byte(str), &list)
	if err != nil {
		return
	}
	if len(list) == 0 {
		err = errors.New("帧格式为空, 请重新编辑")
		return
	}
	return
}
